/*
 * sgi_link_layer_ut.cpp
 *
 *  Created on: 17/10/2011
 *      Author: Santiago Carmona Meco
 */


#include <public/stdlib.h>
#include <public/string.h>
#include <public/stdio.h>
#include <public/sgi_link_layer.h>
#include <public/crc_16.h>
#include <public/sgi_link_layer_ut.h>
#include <public/config.h>
#include <public/debug_primitives.h>
#include <time.h>

#include <iostream>
#include <fstream>
using namespace std;


#define MAX 100
#define NUM_PRUEBAS 200000

SGI_LINK_LAYER_UT Sgi_Link_Layer_Ut("SGI_LINK_LAYER_UT", true, true, 0);


SGI_LINK_LAYER_UT::SGI_LINK_LAYER_UT(const char * id, bool_t priority, bool_t setSeed,
		int32_t seed) : CUTBase(id,priority, setSeed, seed)
{
	// TODO Auto-generated constructor stub

}

SGI_LINK_LAYER_UT::~SGI_LINK_LAYER_UT() {
	// TODO Auto-generated destructor stub
}

bool_t SGI_LINK_LAYER_UT::DoTest(void)
{
	byte_t cabeceraRecibida[16],cabeceraDuplicada[32];
	int numero=0,i=0,j=0,caso;
	struct sgi_link_layer_burst_info_t cabeceraFinal;
	bool_t testCrcOk,testOk=1;
	init_CRC_word16_table();



	while (j<NUM_PRUEBAS)
	{

		Generate_Random_Burst(cabeceraRecibida);

		for (i=0;i<16;i++)
		{
			cabeceraDuplicada[i]=cabeceraRecibida[i];
			cabeceraDuplicada[i+16]=cabeceraRecibida[i];
		}

		numero=rand()%14;
		caso=rand()%4;
		switch (caso)
		{
		case 0://falla el primer CRC
			cabeceraDuplicada[numero]++;
			testCrcOk=sgi_link_layer_get_burst_info(cabeceraDuplicada,&cabeceraFinal);
			if(!testCrcOk)
			{
				DEBUG("FIRST CRC FAIL: ERROR");
				testOk=0;
			}
			else
			{
				if (!Check_Burst_Info(cabeceraDuplicada+16,cabeceraFinal))
				{
					testOk=0;
				}
			}
			cabeceraDuplicada[numero]--;
			break;
		case 1://falla el segundo CRC
			cabeceraDuplicada[numero+16]++;
			testCrcOk=sgi_link_layer_get_burst_info(cabeceraDuplicada,&cabeceraFinal);
			if(!testCrcOk)
			{
				DEBUG("SECOND CRC FAIL: ERROR");
				testOk=0;
			}
			else
			{
				if(!Check_Burst_Info(cabeceraDuplicada,cabeceraFinal))
				{
					testOk=0;
				}
			}
			cabeceraDuplicada[numero+16]--;
			break;
		case 2://fallan los 2 CRCs
			cabeceraDuplicada[numero]++;
			cabeceraDuplicada[numero+16]++;
			testCrcOk=sgi_link_layer_get_burst_info(cabeceraDuplicada,&cabeceraFinal);
			if (testCrcOk)//si fallan los crcs no puede dar OK jamás
			{
				DEBUG("BOTH CRC FAIL: ERROR");
				testOk=0;
			}
			cabeceraDuplicada[numero]--;
			cabeceraDuplicada[numero+16]--;
			break;
		case 3://no falla ningún CRC
			testCrcOk=sgi_link_layer_get_burst_info(cabeceraDuplicada,&cabeceraFinal);
			if(!testCrcOk)
			{
				DEBUG("BOTH CRC OK: ERROR");
				testOk=0;
			}
			else
			{
				if(!Check_Burst_Info(cabeceraDuplicada,cabeceraFinal))
				{
					testOk=0;
				}
			}
			break;
		}
		j++;
	}
	return testOk;

}



bool_t SGI_LINK_LAYER_UT::Check_Codes(byte_t * pBurst, byte_t code)
{
	return 	CompareByte(pBurst[0],code,"error Code");
}

bool_t SGI_LINK_LAYER_UT::Check_Ctrl(byte_t * pBurst, byte_t ctrl)
{
	return 	CompareByte(pBurst[1],ctrl,"error ctrl");
}

bool_t SGI_LINK_LAYER_UT::Check_Key(byte_t * pBurst, byte_t *pKey)
{
	int i=0;

	while (i<4)
	{
		if (!CompareByte(pKey[i],pBurst[i+2],"error Key"))
		{
			return 0;
		}
		i++;
	}
	return 1;
}

bool_t SGI_LINK_LAYER_UT::Check_Univ_Time(byte_t * pBurst, byte_t *pUnivTime)
{
	int i=0;

	while (i<4)
	{
		if (!CompareByte(pUnivTime[i],pBurst[i+6],"error Univ Time"))
		{
			return 0;
		}
		i++;
	}
	return 1;
}




bool_t SGI_LINK_LAYER_UT::Check_Size_Req(byte_t * pBurst, byte_t *pSizeReq)
{
	int i=0;

	while (i<4)
	{
		if (!CompareByte(pSizeReq[i],pBurst[i+6],"error Size Req"))
		{
			return 0;
		}
		i++;
	}
	return 1;
}

bool_t SGI_LINK_LAYER_UT::Check_Size_Provide(byte_t * pBurst, byte_t *pSizeProvide)
{
	int i=0;

	while (i<4)
	{
		if (!CompareByte(pSizeProvide[i],pBurst[i+10],"error Size Provide"))
		{
			return 0;
		}
		i++;
	}
	return 1;
}



bool_t SGI_LINK_LAYER_UT::Check_Stats_Tx(byte_t * pBurst, byte_t *pStatsTx)
{
	int i=0;

	while (i<4)
	{
		if (!CompareByte(pStatsTx[i],pBurst[i+6],"error Stats Rx"))
		{
			return 0;
		}
		i++;
	}
	return 1;
}


bool_t SGI_LINK_LAYER_UT::Check_Stats_Rx(byte_t * pBurst, byte_t *pStatsRx)
{
	int i=0;

	while (i<4)
	{
		if (!CompareByte(pStatsRx[i],pBurst[i+10],"error Stats RX"))
		{
			return 0;
		}
		i++;
	}
	return 1;
}

bool_t SGI_LINK_LAYER_UT::Check_Acks(byte_t * pBurst, byte_t *pAcks)
{
	int i=0;

	while (i<4)
	{
		if (!CompareByte(pAcks[i],pBurst[i+6],"error Acks"))
		{
			return 0;
		}
		i++;
	}
	return 1;
}


bool_t SGI_LINK_LAYER_UT::Check_Gc(byte_t * pBurst, byte_t gc)
{
	return 	CompareByte(pBurst[10],gc,"error GC");
}


bool_t SGI_LINK_LAYER_UT::Check_Ss(byte_t * pBurst, byte_t ss)
{
	return 	CompareByte(pBurst[11],ss,"error SS");
}


bool_t SGI_LINK_LAYER_UT::Check_Sd(byte_t * pBurst, byte_t sd)
{
	return 	CompareByte(pBurst[12],sd,"error SD");
}

bool_t SGI_LINK_LAYER_UT::Check_Nf(byte_t * pBurst, byte_t nf)
{
	return 	CompareByte(pBurst[13],nf,"error NF");
}

bool_t SGI_LINK_LAYER_UT::Check_Burst_Info (byte_t * pBurst,struct sgi_link_layer_burst_info_t burstInfo)
{
	if(!Check_Codes(pBurst,burstInfo.code) ||
	   !Check_Ctrl(pBurst,burstInfo.crtl) ||
	   !Check_Key(pBurst,burstInfo.key))
	{
		return 0;
	}
	switch (burstInfo.code)//no hace falta default, se supone q si entra aquí esta bien
		{//dummy no es necesario
		case MASK_BURST_A_SGI:
			if(!Check_Univ_Time(pBurst,burstInfo.burst_type_info.type_a_info.univ_time) ||
			   !Check_Nf(pBurst,burstInfo.burst_type_info.type_a_info.nf))
			{
				return 0;
			}
			break;
		case MASK_BURST_B_SGI:
			if(!Check_Size_Req(pBurst,burstInfo.burst_type_info.type_b_info.size_req) ||
			   !Check_Size_Provide(pBurst,burstInfo.burst_type_info.type_b_info.size_provide))
			{
				return 0;
			}
			break;
		case MASK_BURST_C_SGI:
			if(!Check_Acks(pBurst,burstInfo.burst_type_info.type_c_info.acks) ||
			   !Check_Nf(pBurst,burstInfo.burst_type_info.type_a_info.nf))
			{
				return 0;
			}
			break;
		case MASK_BURST_D_SGI:
			if(!Check_Acks(pBurst,burstInfo.burst_type_info.type_d_info.acks)||
			   !Check_Gc(pBurst,burstInfo.burst_type_info.type_d_info.gc)    ||
			   !Check_Ss(pBurst,burstInfo.burst_type_info.type_d_info.ss)	 ||
			   !Check_Sd(pBurst,burstInfo.burst_type_info.type_d_info.sd)	 ||
			   !Check_Nf(pBurst,burstInfo.burst_type_info.type_d_info.nf))
			{
				return 0;
			}
			break;
		case MASK_BURST_E_SGI:
			if(!Check_Stats_Tx(pBurst,burstInfo.burst_type_info.type_e_info.stats_tx) ||
			   !Check_Stats_Rx(pBurst,burstInfo.burst_type_info.type_e_info.stats_rx))
			{
				return 0;
			}
			break;
		case MASK_BURST_F_SGI:
			//nada, todo dummy
			break;
		}
	return 1;
}

void SGI_LINK_LAYER_UT::Generate_Random_Burst (byte_t * pBurst)
{
	struct sgi_link_layer_burst_info_t  pBurst_info;
	int i = 0;
	srand (time(NULL));
	pBurst_info.code=rand()%6+10;
	pBurst_info.crtl=rand()%42;
	while (i < 4)
	{
		pBurst_info.key[i]=rand()%42;
		i++;
	}
	switch (pBurst_info.code)
	{
		case MASK_BURST_A_SGI://TODO: aunque el dummy de igual algo habrá que montar para que no sea null
			i=0;
			while (i<4)
			{
				pBurst_info.burst_type_info.type_a_info.univ_time[i]=rand()%42;
				i++;
			}
			i=0;
			while (i<3)
			{
				pBurst_info.burst_type_info.type_a_info.dummy[i]=rand()%42;
				i++;
			}
			pBurst_info.burst_type_info.type_a_info.nf=rand()%42;
			break;//TODO:probar sin el * en el struct de la cabecera
		case MASK_BURST_B_SGI:
			i=0;
			while (i<4)
			{
				pBurst_info.burst_type_info.type_b_info.size_req[i]=rand()%42;
				i++;
			}
			i=0;
			while (i<4)
			{
				pBurst_info.burst_type_info.type_b_info.size_provide[i]=rand()%42;
				i++;
			}
			break;
		case MASK_BURST_C_SGI:
			i=0;
			while (i<4)
			{
				pBurst_info.burst_type_info.type_c_info.acks[i]=rand()%42;
				i++;
			}
			i=0;
			while (i<3)
			{
				pBurst_info.burst_type_info.type_c_info.dummy[i]=rand()%42;
				i++;
			}
			pBurst_info.burst_type_info.type_c_info.nf=rand()%42;
			break;
		case MASK_BURST_D_SGI:
			i=0;
			while (i<4)
			{
				pBurst_info.burst_type_info.type_d_info.acks[i]=rand()%42;
				i++;
			}
			pBurst_info.burst_type_info.type_d_info.gc=rand()%42;;
			pBurst_info.burst_type_info.type_d_info.ss=rand()%42;;
			pBurst_info.burst_type_info.type_d_info.sd=rand()%42;;
			pBurst_info.burst_type_info.type_d_info.nf=rand()%42;;
			break;
		case MASK_BURST_E_SGI:
			i=0;
			while (i<4)
			{
				pBurst_info.burst_type_info.type_e_info.stats_tx[i]=rand()%42;
				i++;
			}
			i=0;
			while (i<4)
			{
				pBurst_info.burst_type_info.type_e_info.stats_rx[i]=rand()%42;
				i++;
			}
			break;
		case MASK_BURST_F_SGI:
			i=0;
			while (i<8)
			{
				pBurst_info.burst_type_info.type_f_info.dummy[i]=rand()%42;
				i++;
			}
			break;
	}
	sgi_link_layer_build_header_burst(pBurst,&pBurst_info);
	sgi_link_layer_add_crc_to_frame(pBurst);

}



#ifdef CONFIG_SGI_LINK_LAYER_UT_SWR_LOG//PONER EN EL DEBUG PRIMITIVE

void SGI_LINK_LAYER_UT::Write_Log_File(const char* mensaje)
{
	ofstream fichero;
	fichero.open("sgiLogFile.txt", ofstream::app);
	time_t tiempo = time(0);
	struct tm *tlocal = localtime(&tiempo);
	char output[128];
	strftime(output,128,"DATE: %d/%m/%y %H:%M:%S RESULT: ",tlocal);
	fichero << output;
	fichero << mensaje;
	fichero << '\n';
	fichero.close();
}

#else

void SGI_LINK_LAYER_UT::Write_Log_File(const char* mensaje)
{
}

#endif





